package com.jvxb.manage.livable.service.impl;

import com.baomidou.mybatisplus.core.conditions.Condition;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.jvxb.common.base.entity.WorkResult;
import com.jvxb.common.base.entity.WorkTask;
import com.jvxb.common.constants.DbConstants;
import com.jvxb.common.constants.DbMonitorIndexConstants;
import com.jvxb.common.constants.MonitorCollectCmdConstants;
import com.jvxb.common.utils.CommonUtil;
import com.jvxb.common.utils.WorkTaskUtil;
import com.jvxb.manage.livable.entity.AssetsAgentHost;
import com.jvxb.manage.livable.entity.AssetsInstance;
import com.jvxb.manage.livable.entity.DbMonitorIndex;
import com.jvxb.manage.livable.service.AssetsAgentHostService;
import com.jvxb.manage.livable.service.AssetsInstanceService;
import com.jvxb.manage.livable.service.dbMonitor.DbMonitorIndexService;
import com.jvxb.manage.livable.util.JdbcUtil;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jvxb.manage.livable.service.DbMonitorDataService;
import com.jvxb.manage.livable.mapper.DbMonitorDataMapper;
import com.jvxb.manage.livable.entity.DbMonitorData;

import java.sql.Connection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author jvxb
 * @since 2022-02-15
 */
@Log4j2
@Service
public class DbMonitorDataServiceImpl extends ServiceImpl<DbMonitorDataMapper, DbMonitorData> implements DbMonitorDataService {

    @Autowired
    AssetsAgentHostService agentHostService;
    @Autowired
    AssetsInstanceService assetsInstanceService;
    @Autowired
    DbMonitorIndexService monitorIndexService;

    ExecutorService executor = Executors.newFixedThreadPool(3);

    @Override
    public void collectAgentMonitorData() {
        List<AssetsAgentHost> agentHostList = agentHostService.list(Condition.<AssetsAgentHost>create()
                .select("distinct host"));
        List<DbMonitorIndex> agentMonitorIndex = monitorIndexService.list(Condition.<DbMonitorIndex>create()
                .eq(DbMonitorIndex.MONI_OBJ_TYPE, DbConstants.HOST)
                .eq(DbMonitorIndex.IN_USE, DbMonitorIndex.IN_USE_YES));
        //采集机器的监控数据
        for (AssetsAgentHost agentHost : agentHostList) {
            executor.execute(() -> agentMonitorDataCollect(agentHost, agentMonitorIndex));
        }
    }

    @Override
    public void collectRedisMonitorData() {
        //获取redis实例
        List<AssetsInstance> redisInstances = assetsInstanceService.list(Condition.<AssetsInstance>create()
                .eq(AssetsInstance.DB_TYPE, DbConstants.REDIS)
                .last(" and host in (select host from assets_agent_host)"));
        List<DbMonitorIndex> redisMonitorIndex = monitorIndexService.list(Condition.<DbMonitorIndex>create()
                .eq(DbMonitorIndex.MONI_OBJ_TYPE, DbConstants.REDIS)
                .eq(DbMonitorIndex.IN_USE, DbMonitorIndex.IN_USE_YES));
        //采集redis实例的监控数据
        for (AssetsInstance instance : redisInstances) {
            executor.execute(() -> redisMonitorDataCollect(instance, redisMonitorIndex));
        }
    }

    @Override
    public void collectMysqlMonitorData() {
        //获取mysql实例、对应指标
        List<AssetsInstance> mysqlInstances = assetsInstanceService.list(Condition.<AssetsInstance>create()
                .eq(AssetsInstance.DB_TYPE, DbConstants.MYSQL)
                .last(" and host in (select host from assets_agent_host)"));
        List<DbMonitorIndex> mysqlMonitorIndex = monitorIndexService.list(Condition.<DbMonitorIndex>create()
                .eq(DbMonitorIndex.MONI_OBJ_TYPE, DbConstants.MYSQL)
                .eq(DbMonitorIndex.IN_USE, DbMonitorIndex.IN_USE_YES));
        //采集mysql实例的监控数据
        for (AssetsInstance instance : mysqlInstances) {
            executor.execute(() -> mysqlMonitorDataCollect(instance, mysqlMonitorIndex));
        }
    }

    private void agentMonitorDataCollect(AssetsAgentHost agentHost, List<DbMonitorIndex> agentMonitorIndex) {
        Date date = new Date();
        for (DbMonitorIndex monitorIndex : agentMonitorIndex) {
            try {
//                log.info("开始采集机器[{}]监控指标[{}]。", agentHost.getHost(), monitorIndex.getMetricName());
                //采集内存 + CPU数据
                if (DbMonitorIndexConstants.CPU_IDLE.equalsIgnoreCase(monitorIndex.getMetricName())) {
                    collectAgentCpuData(agentHost, MonitorCollectCmdConstants.COLLECT_CMD_CPU, date);
                }
                //采集磁盘数据
                else if (DbMonitorIndexConstants.DISK_TOTAL_USED.equalsIgnoreCase(monitorIndex.getMetricName())) {
                    collectAgentDiskData(agentHost, MonitorCollectCmdConstants.COLLECT_CMD_DISK, date);
                }
            } catch (Exception e) {
                log.error("采集机器[{}]监控指标[{}]异常：{}", agentHost.getHost(), monitorIndex.getMetricName(), e.getMessage(), e);
            }
        }
    }

    private void collectAgentCpuData(AssetsAgentHost agentHost, String collectCmdCpu, Date date) {
        WorkResult result = WorkTaskUtil.runAndGetResult(new WorkTask(agentHost.getHost(), collectCmdCpu));
        String loadAvg1 = "";
        String loadAvg5 = "";
        String loadAvg15 = "";
        String cpuid = "";
        String cpuwa = "";
        Double memTotal = 0D;
        Double memUsed = 0D;
        Double memRate = 0D;
        if (CommonUtil.notNullOrEmpty(result.getCode()) && CommonUtil.notNullOrEmpty(result.getData())) {
            //top - 12:18:02 up 108 days, 15:49,  1 user,  load average: 0.00, 0.01, 0.05
            //Tasks: 118 total,   1 running, 116 sleeping,   0 stopped,   1 zombie
            //%Cpu(s):  3.2 us,  0.0 sy,  0.0 ni, 96.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
            //KiB Mem :  3880192 total,   147604 free,  1063328 used,  2669260 buff/cache
            //KiB Swap:        0 total,        0 free,        0 used.  2518232 avail Mem
            String[] topLines = result.getData().split("\n");
            String[] loadAvgs = topLines[0].split("load average:")[1].split(",");
            loadAvg1 = loadAvgs[0].trim();
            loadAvg5 = loadAvgs[1].trim();
            loadAvg15 = loadAvgs[2].trim();
            String cpuLine = topLines[2];
            cpuid = cpuLine.replaceFirst(".+,\\s*(\\S+)\\s+id,.+", "$1");
            cpuwa = cpuLine.replaceFirst(".+,\\s*(\\S+)\\s+wa,.+", "$1");
            String memLine = topLines[3];
            String memTotalStr = memLine.replaceFirst(".+:\\s+(\\d+)\\s+total.+", "$1");
            String memUsedStr = memLine.replaceFirst(".+,\\s+(\\d+)\\s+used.+", "$1");
            String swapLine = topLines[4];
            String swapTotal = swapLine.replaceFirst(".+:\\s+(\\d+)\\s+total.+", "$1");
            String swapUsed = swapLine.replaceFirst(".+,\\s+(\\d+)\\s+used.+", "$1");
            memTotal = Double.valueOf(memTotalStr) + Double.valueOf(swapTotal);
            memUsed = Double.valueOf(memUsedStr) + Double.valueOf(swapUsed);
            memRate = memTotal == 0D ? 0 : (memUsed / memTotal * 100);
        }
        memTotal = memTotal / 1024;
        memUsed = memUsed / 1024;
        //记录机器内存（M）
        UpdateWrapper wrapper = new UpdateWrapper();
        wrapper.eq(AssetsAgentHost.HOST, agentHost.getHost());
        wrapper.set(AssetsAgentHost.MEMORY_ALL, memTotal.longValue());
        wrapper.set(AssetsAgentHost.MEMORY_USED, memUsed.longValue());
        wrapper.set(AssetsAgentHost.MEMORY_FREE, memTotal.longValue() - memUsed.longValue());
        agentHostService.update(agentHost, wrapper);
        //记录机器内存使用量（G）、使用率
        memUsed = memUsed / 1024;
        save(new DbMonitorData(DbConstants.HOST, DbMonitorIndexConstants.MEM_USED, agentHost.getHost(), String.format("%.2f", memUsed), date));
        save(new DbMonitorData(DbConstants.HOST, DbMonitorIndexConstants.MEM_USED_PERCENT, agentHost.getHost(), String.format("%.2f", memRate), date));
        //记录cpu负载、使用率
        save(new DbMonitorData(DbConstants.HOST, DbMonitorIndexConstants.CPU_IDLE, agentHost.getHost(), cpuid, date));
        save(new DbMonitorData(DbConstants.HOST, DbMonitorIndexConstants.CPU_WAIT_IO, agentHost.getHost(), cpuwa, date));
        save(new DbMonitorData(DbConstants.HOST, DbMonitorIndexConstants.CPU_SERVER_LOADAVG_1, agentHost.getHost(), loadAvg1, date));
        save(new DbMonitorData(DbConstants.HOST, DbMonitorIndexConstants.CPU_SERVER_LOADAVG_5, agentHost.getHost(), loadAvg5, date));
        save(new DbMonitorData(DbConstants.HOST, DbMonitorIndexConstants.CPU_SERVER_LOADAVG_15, agentHost.getHost(), loadAvg15, date));
    }

    private void collectAgentDiskData(AssetsAgentHost agentHost, String collectCmdDisk, Date date) {
        WorkResult result = WorkTaskUtil.runAndGetResult(new WorkTask(agentHost.getHost(), collectCmdDisk));
        Double diskTotal = 0D;
        Double diskUsed = 0D;
        Double diskUsedRate = 0D;
        if (CommonUtil.notNullOrEmpty(result.getCode()) && CommonUtil.notNullOrEmpty(result.getData())) {
            //# df -lm|awk '{print $2,$3}'
            //1M-blocks Used
            //1884 0
            //1895 1
            //80506 9145
            //80506 9145
            String[] diskArr = result.getData().split("\n");
            for (int i = 1; i < diskArr.length; i++) {
                String diskLine = diskArr[i];
                String[] diskLineArr = diskLine.split("\\s+");
                diskTotal += Double.valueOf(diskLineArr[0]);
                diskUsed += Double.valueOf(diskLineArr[1]);
            }
            diskUsedRate = diskTotal == 0 ? 0 : (diskUsed / diskTotal * 100);
        }
        //保存机器磁盘（M）
        UpdateWrapper wrapper = new UpdateWrapper();
        wrapper.eq(AssetsAgentHost.HOST, agentHost.getHost());
        wrapper.set(AssetsAgentHost.DISK_ALL, diskTotal.longValue());
        wrapper.set(AssetsAgentHost.DISK_USED, diskUsed.longValue());
        wrapper.set(AssetsAgentHost.DISK_FREE, diskTotal.longValue() - diskUsed.longValue());
        agentHostService.update(agentHost, wrapper);
        //保存机器磁盘使用量（G）、使用率
        diskUsed = diskUsed / 1024;
        save(new DbMonitorData(DbConstants.HOST, DbMonitorIndexConstants.DISK_TOTAL_USED, agentHost.getHost(), String.format("%.2f", diskUsed), date));
        save(new DbMonitorData(DbConstants.HOST, DbMonitorIndexConstants.DISK_TOTAL_USED_PERCENT, agentHost.getHost(), String.format("%.2f", diskUsedRate), date));
    }


    private void mysqlMonitorDataCollect(AssetsInstance instance, List<DbMonitorIndex> agentMonitorIndex) {
        if (CommonUtil.isNullOrEmpty(agentMonitorIndex)) {
            return;
        }
        Date date = new Date();
        Connection connection = null;
        String tag = instance.getHost() + "-" + instance.getPort();
        for (DbMonitorIndex monitorIndex : agentMonitorIndex) {
//            log.info("开始采集MySQL[{}:{}]监控指标[{}]。", agentHost.getHost(), instance.getPort(), monitorIndex.getMetricName());
            try {
                if (connection == null) {
                    connection = JdbcUtil.getConnection(instance.getHost(), instance.getPort());
                }
                //采集连接数
                if (DbMonitorIndexConstants.MYSQL_THREADS_CONNECTED.equalsIgnoreCase(monitorIndex.getMetricName())) {
                    collectMysqlThreadData(tag, connection, MonitorCollectCmdConstants.COLLECT_CMD_MYSQL_THREAD, date);
                } else if (DbMonitorIndexConstants.MYSQL_COM_SELECT.equalsIgnoreCase(monitorIndex.getMetricName())) {
                    collectMysqlDmlData(tag, connection, MonitorCollectCmdConstants.COLLECT_CMD_MYSQL_DML, date);
                }
            } catch (Exception e) {
                log.error("采集MySQL[{}:{}]监控指标[{}]异常：{}", instance.getHost(), instance.getPort(), monitorIndex.getMetricName(), e.getMessage(), e);
            }
        }
        JdbcUtil.close(connection);
    }

    private void collectMysqlDmlData(String tag, Connection connection, String collectCmdMysqlDml, Date date) throws Exception {
        Integer Com_select = 0;
        Integer Com_insert = 0;
        Integer Com_update = 0;
        Integer Com_delete = 0;
        List<Map<String, Object>> queryList = JdbcUtil.queryList(connection, collectCmdMysqlDml);
        for (Map<String, Object> query : queryList) {
            if ("Com_select".equals(query.get("Variable_name"))) {
                Com_select = Integer.valueOf(query.get("Value").toString());
            } else if ("Com_insert".equals(query.get("Variable_name"))) {
                Com_insert = Integer.valueOf(query.get("Value").toString());
            } else if ("Com_update".equals(query.get("Variable_name"))) {
                Com_update = Integer.valueOf(query.get("Value").toString());
            } else if ("Com_delete".equals(query.get("Variable_name"))) {
                Com_delete = Integer.valueOf(query.get("Value").toString());
            }
        }
        save(new DbMonitorData(DbConstants.MYSQL, DbMonitorIndexConstants.MYSQL_COM_SELECT, tag, Com_select.toString(), date));
        save(new DbMonitorData(DbConstants.MYSQL, DbMonitorIndexConstants.MYSQL_COM_INSERT, tag, Com_insert.toString(), date));
        save(new DbMonitorData(DbConstants.MYSQL, DbMonitorIndexConstants.MYSQL_COM_UPDATE, tag, Com_update.toString(), date));
        save(new DbMonitorData(DbConstants.MYSQL, DbMonitorIndexConstants.MYSQL_COM_DELETE, tag, Com_delete.toString(), date));
    }

    private void collectMysqlThreadData(String tag, Connection connection, String collectCmdMysqlThread, Date date) throws Exception {
        Integer threadsConnected = 0;
        Integer threadsRunning = 0;
        List<Map<String, Object>> queryList = JdbcUtil.queryList(connection, collectCmdMysqlThread);
        for (Map<String, Object> query : queryList) {
            if ("Threads_connected".equals(query.get("Variable_name"))) {
                threadsConnected = Integer.valueOf(query.get("Value").toString());
            } else if ("Threads_running".equals(query.get("Variable_name"))) {
                threadsConnected = Integer.valueOf(query.get("Value").toString());
            }
        }
        save(new DbMonitorData(DbConstants.MYSQL, DbMonitorIndexConstants.MYSQL_THREADS_CONNECTED, tag, threadsConnected.toString(), date));
        save(new DbMonitorData(DbConstants.MYSQL, DbMonitorIndexConstants.MYSQL_THREADS_RUNNING, tag, threadsRunning.toString(), date));
    }

    private void redisMonitorDataCollect(AssetsInstance instance, List<DbMonitorIndex> redisMonitorIndex) {

    }

    public static void main(String[] args) {
        WorkTask workTask = new WorkTask();
        workTask.setHost("jvxb.com");
//        workTask.setHost("cxw.com");
        workTask.setCmd("top -b -n 1 | grep 'top -' -A 5");
        WorkResult result = WorkTaskUtil.runAndGetResult(workTask);
        System.out.println(result);
    }
}
